// ========================================================================
// Copyright (c) 2004-2011 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at 
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses. 
// ========================================================================
package org.eclipse.jetty.osgi.boot.jsp;

import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;

import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.TagLibConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;

/**
 * <p>
 * Replacement for {@link TagLibConfiguration} for the OSGi integration.
 * </p>
 * <p>
 * In the case of a WAB, tlds can be located in OSGi bundles that are dependencies
 * of the WAB.
 * It is expected that each WAB lists the symbolic-names of the bundles that contain
 * tld files. The list is defined as the value of the header 'Require-TldBundle'
 * </p>
 * <p>
 * Discussions about this are logged in https://bugs.eclipse.org/bugs/show_bug.cgi?id=306971
 * </p>
 */
public class TagLibOSGiConfiguration extends TagLibConfiguration
{
	private ServiceTracker packageAdminServiceTracker = null;
	
	/**
	 * Override the preConfigure; locates the bundles that contain
	 * tld files according to the value of the manifest header Require-TldBundle.
	 * <p>
	 * Set or add to the property TldProcessor.TLDResources the list of located jars
	 * so that the super class will scan those.
	 * </p>
	 */
    public void preConfigure(WebAppContext context) throws Exception
    {
    	String requireTldBundle = (String)context.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
    	if (requireTldBundle != null)
    	{
    		Collection<Resource> resources = getRequireTldBundleAsJettyResources(context, requireTldBundle);
    		if (resources != null && !resources.isEmpty())
    		{
    			Collection<Resource> previouslySet = (Collection<Resource>)
    				context.getAttribute(TagLibConfiguration.TLD_RESOURCES);
    			if (previouslySet != null)
    			{
    				resources.addAll(previouslySet);
    			}
    			context.setAttribute(TagLibConfiguration.TLD_RESOURCES, resources);
    		}
    	}
    	super.preConfigure(context);

    }

    /**
     * @param requireTldBundle The comma separated list of bundles' symbolic names
     * that contain tld for this osgi webapp.
     * @return The collection of jars or folders that match those bundles.
     */
    private Collection<Resource> getRequireTldBundleAsJettyResources(
    		WebAppContext context, String requireTldBundle)
    {
    	Bundle bundle = (Bundle)
    		context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
    	PackageAdmin packAdmin = getBundleAdmin();
    	String[] symbNames = requireTldBundle.split(", ");
    	Collection<Resource> tlds = new LinkedHashSet<Resource>();
    	for (String symbName : symbNames)
    	{
    		Bundle[] bs = packAdmin.getBundles(symbName, null);
    		if (bs == null || bs.length == 0)
    		{
    			throw new IllegalArgumentException("Unable to locate the bundle '"
    					+ symbName + "' specified in the "
    					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
    					+ " of the manifest of "
    					+ bundle.getSymbolicName());
    		}
    		//take the first one as it is the most recent version?
			Enumeration<URL> en = bs[0].findEntries("META-INF", "*.tld", false);
			boolean atLeastOneTldFound = false;
			while (en.hasMoreElements())
			{
				atLeastOneTldFound = true;
				URL oriUrl = en.nextElement();
				URL url = DefaultFileLocatorHelper.getLocalURL(oriUrl);
				Resource tldResource;
				try
				{
					tldResource = Resource.newResource(url);
				}
				catch (IOException e)
				{
					throw new IllegalArgumentException("Unable to locate the "
							+ "tld resource in '"
	    					+ url.toString()
	    					+ "' in the bundle '" + bs[0].getSymbolicName()
	    					+ "' while registering the "
	    					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
	    					+ " of the manifest of "
	    					+ bundle.getSymbolicName(), e);
				}
				tlds.add(tldResource);
			}
			if (!atLeastOneTldFound)
			{
				Log.warn("No '/META-INF/*.tld' resources were found "
    					+ " in the bundle '" + bs[0].getSymbolicName()
    					+ "' while registering the "
    					+ OSGiWebappConstants.REQUIRE_TLD_BUNDLE
    					+ " of the manifest of "
    					+ bundle.getSymbolicName());
			}
    	}
    	return tlds;
    }
    
	private PackageAdmin getBundleAdmin()
	{
		if (packageAdminServiceTracker == null)
		{
			Bundle bootBundle = ((BundleReference)OSGiWebappConstants.class.getClassLoader()).getBundle();
			packageAdminServiceTracker = new ServiceTracker(bootBundle.getBundleContext(),
					PackageAdmin.class.getName(), null);
			packageAdminServiceTracker.open();
		}
		return (PackageAdmin) packageAdminServiceTracker.getService();
	}
	    
}
